package services

import (
	"bytes"
	"context"
	"gitee.com/igolang/imoney/internal/app/system/entities"
	"gitee.com/igolang/imoney/internal/app/system/keys"
	"gitee.com/igolang/imoney/internal/app/system/services/dict"
	"gitee.com/igolang/imoney/internal/app/system/tools"
	commonEntities "gitee.com/igolang/imoney/internal/common/entities"
	"gitee.com/igolang/imoney/internal/common/httpResp"
	commonServices "gitee.com/igolang/imoney/internal/common/services"
	commonTools "gitee.com/igolang/imoney/internal/common/tools"
	"gitee.com/igolang/imoney/internal/core/client"
	"gitee.com/igolang/imoney/internal/models/dao"
	"gitee.com/igolang/imoney/internal/models/model"
	"gitee.com/igolang/pkg/xgorm"
	"gitee.com/igolang/pkg/xtime"
	"gitee.com/igolang/pkg/xutils"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"gorm.io/gen"
	"time"
)

var Config = &config{}

type config struct{}

func (s *config) List(ctx *gin.Context) (*commonEntities.ListResp, error) {
	var req entities.ConfigPageListReq
	if err := ctx.ShouldBind(&req); err != nil {
		return nil, err
	}
	req.Page()
	conds := s.formatListConds(req)
	total, err := dao.SysConfig.Where(conds...).Count()
	if err != nil {
		return nil, err
	}
	result := &commonEntities.ListResp{
		Total: total,
		Rows:  make([]*model.SysConfig, 0),
	}
	if total == 0 {
		return result, nil
	}
	result.Rows, err = dao.SysConfig.Where(conds...).Order(dao.SysConfig.ConfigID.Desc()).Offset(req.Offset).Limit(req.PageSize).Find()
	if err != nil {
		return nil, err
	}
	return result, err
}

func (s *config) Export(ctx *gin.Context) (b *bytes.Buffer, err error) {
	var req entities.ConfigPageListReq
	if err = ctx.ShouldBind(&req); err != nil {
		return nil, err
	}
	req.Page()
	conds := s.formatListConds(req)
	rows, err := dao.SysConfig.Where(conds...).Order(dao.SysConfig.ConfigID.Desc()).Rows()
	if err != nil {
		return nil, err
	}
	dc := client.DB()
	header := []string{"参数主键", "参数名称", "参数键名", "参数键值", "系统内置"}
	data := [][]string{header}
	yesOptions, _ := dict.Get("sys_yes_no")
	for rows.Next() {
		row := &model.SysConfig{}
		if dc.ScanRows(rows, row) == nil {
			data = append(data, []string{
				cast.ToString(row.ConfigID),
				row.ConfigName,
				row.ConfigKey,
				row.ConfigValue,
				dict.LabelOptions(yesOptions, row.ConfigType),
			})
		}
	}
	fh := xutils.CreateExcelFileWithData(data)
	defer fh.Close()
	return fh.WriteToBuffer()
}

func (s *config) Save(ctx *gin.Context) (err error) {
	req := &model.SysConfig{}
	if err = ctx.ShouldBindJSON(req); err != nil {
		return
	}
	if req.ConfigName == "" || req.ConfigKey == "" || req.ConfigValue == "" {
		return httpResp.ErrorCode(httpResp.ErrCodeInvalidParams)
	}
	conds := []gen.Condition{dao.SysConfig.ConfigKey.Eq(req.ConfigKey)}
	if req.ConfigID > 0 {
		conds = append(conds, dao.SysConfig.ConfigID.Neq(req.ConfigID))
	}
	total, _ := dao.SysConfig.Where(conds...).Count()
	if total > 0 {
		return errors.Errorf("保存参数'%s'失败，参数键名已存在", req.ConfigName)
	}
	if req.ConfigType == "" {
		req.ConfigType = "N"
	}
	userInfo := tools.GetUserInfo(ctx)
	req.UpdateTime = xtime.Time(time.Now())
	req.UpdateBy = userInfo.User.UserName
	if req.ConfigID == 0 {
		req.CreateBy = userInfo.User.UserName
		req.CreateTime = xtime.Time(time.Now())
		err = dao.SysConfig.Create(req)
		if err == nil {
			s.setCache(req.ConfigKey)
		}
		return
	}
	row, err := dao.SysConfig.Where(dao.SysConfig.ConfigID.Eq(req.ConfigID)).Take()
	if err != nil {
		return err
	}
	row.UpdateTime = req.UpdateTime
	row.UpdateBy = req.UpdateBy
	row.ConfigName = req.ConfigName
	row.ConfigKey = req.ConfigKey
	row.ConfigType = req.ConfigType
	row.ConfigValue = req.ConfigValue
	row.Remark = req.Remark
	data := xgorm.ModelToMap(row)
	//log.Printf("%#v\n", data)
	_, err = dao.SysConfig.Where(dao.SysConfig.ConfigID.Eq(req.ConfigID)).Updates(data)
	if err == nil {
		s.setCache(req.ConfigKey)
	}
	return
}

func (s *config) RefreshCache(ctx *gin.Context) error {
	rows, err := dao.SysConfig.Select(dao.SysConfig.ConfigKey, dao.SysConfig.ConfigValue).Rows()
	if err != nil {
		return err
	}
	dc := client.DB()
	rc := client.Redis()
	for rows.Next() {
		row := &model.SysConfig{}
		if dc.ScanRows(rows, row) == nil {
			rk := keys.Config(row.ConfigKey)
			rc.Set(context.Background(), rk.Key, row.ConfigValue, rk.Expire)
		}
	}

	return nil
}

func (s *config) Delete(ctx *gin.Context) error {
	ids := commonTools.String2Int64s(ctx.Param("ids"))
	if len(ids) == 0 {
		return httpResp.ErrorCode(httpResp.ErrCodeInvalidParams)
	}
	rows, err := dao.SysConfig.Select(dao.SysConfig.ConfigKey, dao.SysConfig.ConfigType).Where(dao.SysConfig.ConfigID.In(ids...)).Find()
	if err != nil {
		return err
	}
	for _, v := range rows {
		if v.ConfigType == "Y" {
			return httpResp.ErrWarning("内置参数【%s】不能删除", v.ConfigKey)
		}
	}
	_, err = dao.SysConfig.Where(dao.SysConfig.ConfigID.In(ids...)).Delete()
	if err == nil {
		for _, v := range rows {
			s.cleanCache(v.ConfigKey)
		}
	}

	return err
}

func (s *config) cleanCache(key string) {
	commonServices.Config.CleanCache(key)
}

func (s *config) setCache(key string) string {
	return commonServices.Config.SetCache(key)
}

func (s *config) formatListConds(req entities.ConfigPageListReq) []gen.Condition {
	conds := make([]gen.Condition, 0)
	if req.ConfigName != "" {
		conds = append(conds, dao.SysConfig.ConfigName.Like(`%`+req.ConfigName+`%`))
	}

	if req.ConfigKey != "" {
		conds = append(conds, dao.SysConfig.ConfigKey.Like(`%`+req.ConfigKey+`%`))
	}

	if req.ConfigType != "" {
		conds = append(conds, dao.SysConfig.ConfigType.Eq(req.ConfigType))
	}

	if !req.BT.IsZero() && !req.ET.IsZero() {
		conds = append(conds, dao.SysConfig.CreateTime.Gte(req.BT), dao.SysConfig.CreateTime.Lte(req.ET))
	}
	return conds

}
func (s *config) Get(key string) string {
	return commonServices.Config.Get(key)
}

func (s *config) CaptchaEnabled() bool {
	return cast.ToBool(s.Get("sys.account.captchaEnabled"))
}
